﻿using TCC.Framework;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using System.Text;
using UnityEngine.Networking;

namespace TCC.Framework
{
    /// <summary>
    /// 版本管理器
    /// </summary>
    public class VersionsManager : BaseManager
    {
        /// <summary>
        /// 超时时间
        /// </summary>
        public const int DOWNLOADTIMEOUT = 5;

        /// <summary>
        /// 下载器数量
        /// </summary>
        public int DownloadRoutinneNum { get; private set; }

        private AssetbundleDownloadRoutine[] _routine;

        private static int _routineIndex = 0;//下载器索引

        private bool _isDownloadOver = false;

        public bool IsDownloadOver => _isDownloadOver;

        public float TotalSize
        {
            get
            {
                float szie = 0;
                _NeedDownloadResList.ForEach(p => szie += (float)p.m_size);
                return szie;
            }
        }

        /// <summary>
        /// 当前已下载大小
        /// </summary>
        public double _currCompleteTotalSize
        {
            get
            {
                double completeTotalSize = 0;
                for (int i = 0; i<_routine.Length; i++)
                {
                    if (_routine[i] == null) continue;
                    completeTotalSize += _routine[i].DowloadSize;
                }
                return completeTotalSize;
            }
        }

        public int TotalCount => _NeedDownloadResList.Count;

        /// <summary>
        /// 当前已下载数量
        /// </summary>
        public int CurrCompleteTotalCount
        {
            get
            {
                int completeTotalCount = 0;
                for (int i = 0; i < _routine.Length; i++)
                {
                    if (_routine[i] == null) continue;
                    completeTotalCount += _routine[i].CompoleteCount;
                }
                return completeTotalCount;
            }
        }

        /// <summary>
        ///本地资源存放路径
        /// </summary>
        public string LocalResFilePath { get; private set; }

        /// <summary>
        /// 版本文件名字
        /// </summary>
        private const string VERSIONFILE = "VersionFile.json";

        /// <summary>
        /// 本地版本文件存放路径
        /// </summary>
        private string _loaclVersionPath;

        /// <summary>
        /// 待下载资源列表
        /// </summary>
        private List<VersionDataEntity> _NeedDownloadResList = new List<VersionDataEntity>();
        /// <summary>
        /// 本地资源列表
        /// </summary>
        private List<VersionDataEntity> _LocalResList = new List<VersionDataEntity>();

        private MonoBehaviour _mono;

        /// <summary>
        /// 检查版本文件进行事件
        /// </summary>
        private TAction<float> OnCheckVersion;
        /// <summary>
        /// 版本文件检查完成事件
        /// </summary>
        private TAction<float> OnCheckVersionOver;

        /// <summary>
        /// 版本文件检查出错事件
        /// </summary>
        private TAction<string> OnCheckVersionError;
        /// <summary>
        /// 检查进度
        /// </summary>
        private float _checkProgress;

        private TAction<float> OnResDownloading;
        private TAction OnResDownloadOver;

        public VersionsManager(MonoBehaviour mono, int downloadRoutinneNum):base()
        {
            _mono = mono;
            DownloadRoutinneNum = downloadRoutinneNum;
            _routine = new AssetbundleDownloadRoutine[DownloadRoutinneNum];
        }

        public override void OnInit()
        {
            base.OnInit();
            LocalResFilePath = Application.persistentDataPath + "/";
        }

        /// <summary>
        /// 检查版本文件
        /// </summary>
        public void InitCheckVersion(TAction<float> checkVersion,TAction<float> checkVersionOver,TAction<string> checkVersionError=null)
        {
            if(string.IsNullOrEmpty(AssetConfig.ResUrl))
            {
                Log.Error("未指定资源服务器地址");
                return;
            }
            OnCheckVersion = checkVersion;
            OnCheckVersionOver = checkVersionOver;
            OnCheckVersionError = checkVersionError;
            _checkProgress = 0;
            string strVersionPath = AssetConfig.ResUrl+ VERSIONFILE;
            _mono.StartCoroutine(DowloadVersion(strVersionPath));
        }

        /// <summary>
        /// 对比版本文件
        /// </summary>
        /// <param name="arg0"></param>
        private void OnInitVersionCallBack(List<VersionDataEntity> arg0)
        {
            _loaclVersionPath = LocalResFilePath + VERSIONFILE;

            if (FileOperations.FileExists(_loaclVersionPath))
            {
                List<VersionDataEntity> clienData = FileOperations.ReadJsonData<List<VersionDataEntity>>(_loaclVersionPath);

                foreach (var item in arg0)
                {
                    _checkProgress += 1.0f / arg0.Count / 2;
                    _checkProgress = _checkProgress >= 1 ? 1 : _checkProgress;
                    OnCheckVersion?.Invoke(_checkProgress);
                    VersionDataEntity dataEntity = clienData.Find(p => p.m_fullName == item.m_fullName);
                    //已有资源
                    if (dataEntity != null)
                    {
                        //对比MD5
                        if (dataEntity.m_md5 != item.m_md5)
                            _NeedDownloadResList.Add(item);
                    }
                    else
                        _NeedDownloadResList.Add(item);//新资源
                }
            }
            else
                arg0.ForEach(p =>
                {
                    _NeedDownloadResList.Add(p);
                    _checkProgress += 1.0f / arg0.Count / 2;
                    _checkProgress = _checkProgress >= 1 ? 1 : _checkProgress;
                    OnCheckVersion?.Invoke(_checkProgress);
                });

            if (_NeedDownloadResList.Count > 0)
                OnCheckVersionOver?.Invoke(TotalSize);
            else
                OnCheckVersionOver?.Invoke(0);
        }

        /// <summary>
        /// 更新版本文件
        /// </summary>
        /// <param name="entity"></param>
        public void ModifyLocaData(VersionDataEntity entity)
        {
            if (_LocalResList == null) return;
            bool isExists = false;
            for (int i = 0; i < _LocalResList.Count; i++)
            {
                if (_LocalResList[i].m_fullName.Equals(entity.m_fullName, StringComparison.CurrentCultureIgnoreCase))
                {
                    _LocalResList[i].m_md5 = entity.m_md5;
                    _LocalResList[i].m_fullName = entity.m_fullName;
                    _LocalResList[i].m_size = entity.m_size;
                    isExists = true;
                    break;
                }
            }

            if (!isExists)
            {
                _LocalResList.Add(entity);
            }
            SaveLoaclVersion();
        }
        /// <summary>
        /// 保存版本文件
        /// </summary>
        private void SaveLoaclVersion() => FileOperations.WriteJsonData(_LocalResList, _loaclVersionPath);

        /// <summary>
        /// 下载版本文件
        /// </summary>
        /// <param name="url"></param>
        /// <param name="OnDownLoadOver"></param>
        /// <returns></returns>
        private IEnumerator DowloadVersion(string url)
        {
            UnityWebRequest www = UnityWebRequest.Get(url);

            www.timeout = DOWNLOADTIMEOUT;
            yield return www.SendWebRequest();
            while (!www.isDone)
            {
                _checkProgress = www.downloadProgress/2;
                OnCheckVersion?.Invoke(_checkProgress);
            }
             
            if (www.result != UnityWebRequest.Result.ProtocolError && www.result != UnityWebRequest.Result.ConnectionError)
            {
                string content = www.downloadHandler.text;
                _checkProgress = 0.5f;
                OnInitVersionCallBack(content.JsonStrToObject<List<VersionDataEntity>>());
                Debug.Log(content);
            }
            else
            {
                Debug.Log("下载失败：" + www.error);
                OnCheckVersionError?.Invoke(www.error);
            }
        }


        /// <summary>
        /// 分配下载任务并开始下载
        /// </summary>
        /// <param name="downloadList"></param>
        public void DownloadFiles(TAction<float> downloading,TAction downloadOver,TAction<string> downloadError=null)
        {
            OnResDownloading = downloading;
            OnResDownloadOver = downloadOver;
            for (int i = 0; i < _routine.Length; i++)
                _routine[i] = _routine[i] ?? new AssetbundleDownloadRoutine(_mono, AssetConfig.ResUrl);

            for (int i = 0; i < _NeedDownloadResList.Count; i++)
            {
                _routineIndex = _routineIndex % _routine.Length;
                _routine[_routineIndex].AddDownload(_NeedDownloadResList[i]);
                _routineIndex++;
            }
            for (int i = 0; i < _routine.Length; i++)
            {
                if (_routine[i] == null) continue;
                _routine[i].OnDownloadOver = OnDownloadOver;
                //_routine[i].OnDownloading = OnDownloading;
                _routine[i].OnResDownloadError = downloadError;
                _routine[i].StartDowload();
            }

            _mono.StartCoroutine(OnDownloading());
        }

        /// <summary>
        /// 下载中
        /// </summary>
        private IEnumerator OnDownloading()
        {
            yield return new WaitUntil(() =>
            {
                string str = $"正在下载{_currCompleteTotalSize}/{TotalSize} ";
                Debug.Log(str);
                OnResDownloading?.Invoke((float)_currCompleteTotalSize);
                return _currCompleteTotalSize >= TotalSize;
            });

            OnResDownloadOver?.Invoke();
            Debug.Log("下载完毕！");
        }

        /// <summary>
        /// 单个任务下载完成事件
        /// </summary>
        /// <param name="data"></param>
        /// <param name="versionData"></param>
        private void OnDownloadOver(byte[] data,VersionDataEntity versionData)
        {
            int index = versionData.m_fullName.LastIndexOf('\\');
            string path = index < 0 ? "" : versionData.m_fullName.Substring(0, index);
            string localFilePath = Path.Combine(LocalResFilePath , path);
            localFilePath = localFilePath.Replace("\\", "/");
            if (!Directory.Exists(localFilePath))
                Directory.CreateDirectory(localFilePath);

            string assetPath = Path.Combine(LocalResFilePath, versionData.m_fullName);
            assetPath = assetPath.Replace("\\", "/");
            using (FileStream fs = new FileStream(assetPath, FileMode.Create, FileAccess.Write))
            {
                fs.Write(data, 0, data.Length);
            }

            ModifyLocaData(versionData);
        }
    }
}